package br.ufla.pgcc.oomeasures.astvisitors;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.jdt.core.dom.ASTVisitor;
import org.eclipse.jdt.core.dom.Block;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Statement;
import org.eclipse.jdt.core.dom.TypeDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;

public class LackOfCohesionOfMethodsVisitor extends ASTVisitor{
	
	private int lcomIndex = 0;
	
	private List<MethodDeclaration> listOfMethods = new ArrayList<MethodDeclaration>();
	private List<FieldDeclaration> listOfAttributes = new ArrayList<FieldDeclaration>();
	
	@Override
	public boolean visit(CompilationUnit unit) {
		
		calculateNumberOfMethods(unit);
		
		calculateNumberOfAttributes(unit);
		
		calculateLCOM(unit);
		
		return super.visit(unit);
	}
	
	@SuppressWarnings("unchecked")
	private void calculateLCOM(CompilationUnit unit){
		
		VariableDeclarationFragment attributeName= null;
		int sharedAttributesAccess = 0;
		int notShareAttributesAccess = 0;
		int hasAccess = 0;
		int noAccess = 0;
		
		for (Object type :unit.types()){
			if (type instanceof TypeDeclaration){
				
				for (FieldDeclaration field : listOfAttributes){
					
					List<VariableDeclarationFragment> fieldFragment = field.fragments();
					attributeName = fieldFragment.get(0);
					
					Iterator<MethodDeclaration> itMethods = this.listOfMethods.iterator();
					
					while (itMethods.hasNext()){
						
						MethodDeclaration firstMethod = (MethodDeclaration) itMethods.next();
						Block firstMethodBody = firstMethod.getBody();
						
						if (firstMethodBody != null){
						
							List<Statement> firstMethodStatements = firstMethodBody.statements();
							
							if (!firstMethodStatements.isEmpty()){
							
								if (firstMethodStatements.toString().contains(attributeName.toString())){
									sharedAttributesAccess++;
								}else{
									notShareAttributesAccess++;
								}
							}
						}
					}
					hasAccess += sharedAttributesAccess/2;
					noAccess += notShareAttributesAccess/2;
				}
			}
		}
		lcomIndex = noAccess - hasAccess;
	}
	
	private void calculateNumberOfMethods(CompilationUnit unit){
		for (Object type :unit.types()){
			if (type instanceof TypeDeclaration){
				
				MethodDeclaration [] methods = ((TypeDeclaration) type).getMethods();
				
				for (MethodDeclaration method: methods){
					this.listOfMethods.add(method);
				}
			}
		}
	}
	
	private void calculateNumberOfAttributes(CompilationUnit unit){
		for (Object type :unit.types()){
			if (type instanceof TypeDeclaration){
				
				FieldDeclaration [] attributes = ((TypeDeclaration) type).getFields();
				
				for (FieldDeclaration attribute : attributes){
					this.listOfAttributes.add(attribute);
				}
			}
		}
	}
	
	public int getLcomValue(){
		
		if (lcomIndex > 0)
			return lcomIndex;
		
		return 0;
	}
	
}
